﻿using System;
using System.Collections.Generic;
using System.Collections.Concurrent;
using System.Linq;
using System.Linq.Dynamic;
using System.Linq.Expressions;
using System.Text;
using System.Data;
using System.Data.Common;
using JXT.PrimaryKey.Batman.Collections;
using JXT.PrimaryKey.Batman.Reflection;
using JXT.PrimaryKey.Batman.Core.Configuration;
using JXT.PrimaryKey.Batman.Domain.Models;
using JXT.PrimaryKey.Batman.Domain.Specifications;
using JXT.PrimaryKey.Batman.Domain.Specifications.Expressions;
using JXT.PrimaryKey.Batman.Domain.Repositories.MySql.Expressions;

namespace JXT.PrimaryKey.Batman.Domain.Repositories.MySql
{
    public abstract class MySqlRepositoryBase<TEntity, TKey> : IRepository<TEntity>,
        IReadRepository<TEntity, TKey>,
        IWriteRepository<TEntity, TKey>,
        ISpecificationRepository<TEntity>
        where TEntity : IEntity, new()
    {
        private static readonly string _defaultTableName = typeof(TEntity).Name.ToLower();

        protected MySqlRepositoryContext Context { get; private set; }

        public MySqlRepositoryBase(MySqlRepositoryContext context)
        {
            Context = context;
        }

        RepositoryContext IRepository<TEntity>.Context
        {
            get { return Context; }
        }

        protected ServiceConfigurationSection ServiceConfigration
        {
            get
            {
                return SupernovaConfigurationManager.GetServiceConfigurationSection();
            }
        }

        public virtual void Add(TEntity entity)
        {
            InsertQuery insertQuery = new InsertQuery();
            insertQuery.Append(entity);
            object key = Context.ExecuteScalar(insertQuery.QueryText, insertQuery.Parameters);
            var item = entity.GetMetadataCacheItem("Id");
            if (item != null)
            {
                item.Property.Set(entity, Convert.ChangeType(key, item.Property.Property.PropertyType));
            }
        }

        public virtual void Update(TEntity entity)
        {
            UpdateQuery updateQuery = new UpdateQuery();
            updateQuery.Append(entity);
            Context.ExecuteNonQuery(updateQuery.QueryText, updateQuery.Parameters);
        }

        public virtual void Delete(TKey key)
        {
            string sql = "delete from `{0}` where `id`=@id";
            sql = String.Format(sql, _defaultTableName);
            Context.ExecuteNonQuery(sql, new[] { Context.CreateParameter("@id", key) });
        }

        public virtual bool Exists(TKey key)
        {
            string sql = "select count(*) from `{0}` where `id`=@id";
            sql = String.Format(sql, _defaultTableName);
            int count = Convert.ToInt32(Context.ExecuteScalar(sql, new[] { Context.CreateParameter("@id", key) }));
            return count > 0;
        }

       

        public virtual int Count(ISpecification<TEntity> specification)
        {
            var expressionSpecification = specification.ToExpressionSpecification();
            string sql = "select count(*) from `{0}`";
            sql = String.Format(sql, _defaultTableName);
            IDataParameter[] parameters = null;
            if (expressionSpecification!= null && expressionSpecification.WhereExpression != null)
            {
                WhereQuery<TEntity> query = new WhereQuery<TEntity>();
                query.And(expressionSpecification.WhereExpression);
                sql += " where " + query.QueryText;
                parameters = query.Parameters;
            }
            return Convert.ToInt32(Context.ExecuteScalar(sql, parameters));
        }

        public virtual TEntity GetSingle(TKey key)
        {
            string sql = "select * from `{0}` where `id`=@id";
            sql = String.Format(sql, _defaultTableName);
            TEntity result = default(TEntity);
            using (var dr = Context.ExecuteDataReader(sql, new[] { Context.CreateParameter("@id", key) }))
            {
                if (dr.Read())
                {
                    result = ReadEntityFromDataReader(dr);
                }
            }
            return result;
        }

         

        public virtual IEnumerable<TEntity> GetList()
        {
            return GetList(null);
        }

        public virtual TEntity GetSingle(ISpecification<TEntity> specification)
        {
            var expressionSpecification = specification.ToExpressionSpecification();
            IDataParameter[] parameters = null;
            string sql = expressionSpecification.BuildSingleQueryString(out parameters);
            TEntity result = default(TEntity);
            using (var dr = Context.ExecuteDataReader(sql, parameters))
            {
                if (dr.Read())
                {
                    result = ReadEntityFromDataReader(dr);
                }
            }
            return result;
        }

        public virtual IPagedCollection<TEntity> GetList(IPagedSpecification<TEntity> specification)
        {
            var expressionSpecification = specification.ToExpressionSpecification();
            IDataParameter[] parameters = null;
            string sql = expressionSpecification.BuildQueryString(out parameters);
            IList<TEntity> list = new List<TEntity>();
            using (var dr = Context.ExecuteDataReader(sql, parameters))
            {
                while (dr.Read())
                {
                    var item = ReadEntityFromDataReader(dr);
                    list.Add(item);
                }
            }
            int pageIndex = specification == null ? 0 : specification.PageIndex;
            int pageSize = specification == null ? 0 : specification.PageSize;
            return new PagedCollection<TEntity>(Count(specification), pageIndex, pageSize, list);
        }

        /// <summary>
        /// 将datareader转换为模型对象
        /// </summary>
        /// <remarks>
        /// 转换根据查询结果列明和模型属性名的一致性来决定，不区分大小写
        /// </remarks>
        /// <typeparam name="T">模型对象类型</typeparam>
        /// <param name="dr">datareader对象</param>
        /// <returns>转换成功的模型对象</returns>
        protected virtual T ReadFromDataReader<T>(IDataReader dr) where T : new()
        {
            T result = new T();
            var properties = result.GetMetadataCacheItems();
            for (int i = 0; i < dr.FieldCount; i++)
            {
                string name = dr.GetName(i);
                object value = dr.SafeRead<object>(name);
                var find = properties.FirstOrDefault(e => e.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase));
                if (find != null)
                {
                    if (value == null)
                    {
                        find.Property.Set(result, null);
                    }
                    else if (find.Property.Property.PropertyType.IsEnum)
                    {
                        find.Property.Set(result, Enum.ToObject(find.Property.Property.PropertyType, value));
                    }
                    else if (find.Property.Property.PropertyType == typeof(string))
                    {
                        find.Property.Set(result, Convert.ToString(value).Trim());
                    }
                    else if (find.Property.Property.PropertyType.IsGenericType && find.Property.Property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>) && find.Property.Property.PropertyType.GetGenericArguments().Length > 0)
                    {
                        var gType = find.Property.Property.PropertyType.GetGenericArguments()[0];
                        var gValue = Convert.ChangeType(value, gType);
                        find.Property.Set(result, gValue);
                    }
                    else
                    {
                        find.Property.Set(result, Convert.ChangeType(value, find.Property.Property.PropertyType));
                    }
                }
            }
            return result;
        }

        protected virtual TEntity ReadEntityFromDataReader(IDataReader dr)
        {
            return ReadFromDataReader<TEntity>(dr);
        }

        protected virtual TRepository LoadRepository<TRepository>()
        {
            return JXT.PrimaryKey.Batman.Container.ContainerManager.Repository.Resolve<TRepository>();
        }
    }
}
